/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.config.console.sources;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.util.CollectionUtils;
import org.tianyun.cloud.config.console.ConsoleManager;
import org.tianyun.cloud.config.console.property.Component;
import org.tianyun.cloud.config.console.property.Namespace;
import org.tianyun.cloud.config.console.property.Route;
import org.tianyun.cloud.utils.RegExUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 控制台属性源，可用于控制台对配置进行动态刷新。
 *
 * @author ebert_chan
 */
public class ConsolePropertySource extends EnumerablePropertySource<Map<String, Namespace>> {

    private List<Route> routes;

    private String[] propertityNamesCache;

    public ConsolePropertySource(String name) {
        super(name, new HashMap<>());
    }

    @Override
    public String[] getPropertyNames() {
        if (propertityNamesCache != null) {
            return propertityNamesCache;
        }
        if (CollectionUtils.isEmpty(getSource())) {
            return propertityNamesCache = new String[0];
        }

        Set<String> keys = new HashSet<>();
        for (Route route : getRoutes()) {
            Namespace namespace = getSource().get(route.getNamespace());
            if (namespace == null || !namespace.containsPropertySource(route.getComponent())) {
                continue;
            }

            // 从命名空间获取属性源
            NameWildcardPropertySource nameWildcardPropertySource = namespace.getPropertySource(route.getComponent());
            if (nameWildcardPropertySource == null) {
                continue;
            }

            String[] propertyNames = nameWildcardPropertySource.getPropertyNames();
            if (propertyNames == null || propertyNames.length == 0) {
                continue;
            }

            Component component = ConsoleManager.getComponent(route.getComponent());
            if (StringUtils.isEmpty(component.getConfigurationPrefix())) {
                keys.addAll(Arrays.asList(propertyNames));
            } else {
                keys.addAll(Arrays.asList(propertyNames).stream().map(name -> component.getConfigurationPrefix() + "." + name).collect(Collectors.toList()));
            }
        }
        return propertityNamesCache = keys.stream().toArray(String[]::new);
    }

    @Override
    public boolean containsProperty(String name) {
        boolean contains = super.containsProperty(name);
        if (contains) {
            return true;
        }
        for (String propertyName : getPropertyNames()) {
            if (RegExUtils.containsWildcardCharacter(propertyName)) {
                Pattern pattern = RegExUtils.wildcardCharacterExpression(propertyName, RegExUtils.DOT, true, true);
                if (pattern.matcher(name).find()) {
                    return true;
                }
            }
        }
        return false;
    }

    public List<Route> getRoutes() {
        if (routes != null) {
            return routes;
        }
        return routes = ConsoleManager.routes();
    }

    @Override
    public Object getProperty(String name) {
        if (getSource().size() == 0 || getRoutes().size() == 0) {
            return null;
        }
        return getPropertyByName(name);
    }

    /**
     * 获取属性结果
     *
     * @param name
     * @return java.lang.Object
     */
    private Object getPropertyByName(String name) {
        // 循环路由项，对属性名称进行匹配
        for (Route route : getRoutes()) {
            // 根据路由获取命名空间，如果为空，则不需要处理
            Namespace namespace = getSource().get(route.getNamespace());
            if (namespace == null || !namespace.containsPropertySource(route.getComponent())) {
                continue;
            }

            // 对属性名称进行匹配，判断是否是获取对应路由关联的属性源的结果
            Matcher matcher = route.getExpression().matcher(name);
            if (!matcher.find()) {
                continue;
            }

            // 从命名空间获取属性源
            NameWildcardPropertySource nameWildcardPropertySource = namespace.getPropertySource(route.getComponent());

            // 去除前缀后的属性名
            String subName = matcher.group(route.getGroup());
            // 根据属性名获取结果
            Object value = nameWildcardPropertySource.getProperty(subName);
            if (value == null) {
                continue;
            }

            return value;
        }

        return null;
    }

    /**
     * 保存或替换命名空间属性源
     *
     * @param namespace
     */
    public void putNamespace(Namespace namespace) {
        synchronized (getSource()) {
            getSource().put(namespace.getName(), namespace);
        }
    }

    /**
     * 删除命名空间
     *
     * @param name
     */
    public void removeNamespace(String name) {
        synchronized (getSource()) {
            getSource().remove(name);
        }
    }

}
